package cn.kerui.auth.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.kerui.auth.service.IAuthStrategy;
import cn.kerui.auth.service.LoginService;
import cn.kerui.auth.vo.LoginBody;
import cn.kerui.auth.vo.LoginVo;
import cn.kerui.common.captcha.properties.CaptchaProperties;
import cn.kerui.common.core.constant.AuthConstants;
import cn.kerui.common.core.message.LoginMessage;
import cn.kerui.common.enums.LoginType;
import cn.kerui.common.exception.AuthException;
import cn.kerui.common.framework.util.lang.ServletUtil;
import cn.kerui.common.framework.util.validation.Assert;
import cn.kerui.common.redis.constants.AuthRedisConstants;
import cn.kerui.common.redis.helper.RedisHelper;
import cn.kerui.common.satoken.domain.LoginUser;
import cn.kerui.common.satoken.helper.TokenHelper;
import cn.kerui.common.satoken.utils.StpUtil;
import cn.kerui.repos.entities.manage.ManageUserLogin;
import cn.kerui.repos.mapper.manage.ManageUserLoginMapper;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Optional;

import static cn.kerui.common.enums.AuthExceptionMsg.*;

/**
 * <p> 密码登陆业务层 </p>
 * <p>创建于 2023/12/25 10:32 </p>
 *
 * @author yangkai
 * @version v1.0
 * @since 1.0.0
 */
@Service
@AllArgsConstructor
@Slf4j
public class PasswordAuthStrategy implements IAuthStrategy {

    private ManageUserLoginMapper manageUserLoginMapper;

    private RedisHelper redisHelper;

    private CaptchaProperties captchaProperties;

    private LoginService loginService;

    
    @Override
    public LoginVo login(String body, SaLoginModel loginModel) {
        LoginBody loginBody = JSON.parseObject(body, LoginBody.class);

        // 验证码校验
        if (captchaProperties.getEnable()) {
            validCaptchaCode(loginBody);
        }
        // 判断用户名是否存在
        ManageUserLogin manageUserLogin = getUserInfo(Optional.ofNullable(loginBody.getUsername()));
        // 判断密码是否符合规则校验
        loginService.checkLogin(LoginType.PASSWORD, loginBody.getUserTerminal().getSerializableValue(), loginBody.getUsername(),
                () -> loginService.checkPw(loginBody.getPassword(), manageUserLogin));
        // 用户登录
        LoginUser loginUser = new LoginUser(manageUserLogin.getId(), loginBody.getUserTerminal(), manageUserLogin.getUserName());
        TokenHelper.generateToken(loginUser, loginModel);

        // 记录登录信息 远程事件发布
        loginService.recordLoginLog(loginBody.getUsername(), loginModel.getDevice(), loginBody.getGrantType(), AuthConstants.LOGIN_SUCCESS, LoginMessage.LOGIN_SUCCESS);
        //  更新上次登录IP
        updateLoginIp(manageUserLogin, ServletUtil.getClientIP(ServletUtil.getRequest()));
        // 构建返回值
        LoginVo loginVo = new LoginVo();
        loginVo.setAccToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        return loginVo;
    }

    /**
     * 校验验证码
     * @param loginBody 登录请求体
     */
    private void validCaptchaCode(LoginBody loginBody) {
        try {
            // 判断验证码是否正确
            String code = Optional.ofNullable(loginBody.getCode())
                    .orElseThrow(() -> new AuthException(CAPTCHA_NULL));

            String uuid = Optional.ofNullable(loginBody.getUuid())
                    .orElseThrow(() -> new AuthException(CAPTCHA_NOT_EXIST));
            String rightCode = redisHelper.getObj(AuthRedisConstants.CAPTCHA_KEY, uuid, String.class);

            Assert.isNotBlank(rightCode, () -> new AuthException(CAPTCHA_EXPIRED));
            // 校验
            Assert.isEquals(code, rightCode, () -> new AuthException(CAPTCHA_ERROR));
        } catch (Exception e) {
            loginService.recordLoginLog(loginBody.getUsername(), loginBody.getUserTerminal().getSerializableValue(), loginBody.getGrantType(), AuthConstants.LOGIN_FAILED, e.getMessage());
            throw new AuthException(e.getMessage());
        }
    }


    /**
     * 校验用户信息
     * @param userName
     */
    public ManageUserLogin getUserInfo(Optional<String> userName) {
        if (!userName.isPresent()) {
            throw new AuthException(USER_INFO_INVALID);
        }

        LambdaQueryWrapper<ManageUserLogin> queryWrapper =
                Wrappers.query(ManageUserLogin.class).lambda().eq(ManageUserLogin::getUserName, userName.get());
        ManageUserLogin manageUserLogin = null;
        Optional<ManageUserLogin> optional = null;
        try {
            manageUserLogin = manageUserLoginMapper.selectOne(queryWrapper);
            optional = Optional.of(manageUserLogin);
        } catch (Exception e) {
            throw new AuthException(e.getMessage());
        }

        return optional.get();
    }

    /**
     * 更新用户登录IP
     * @param userId
     * @param clientIp
     */
    private void updateLoginIp(ManageUserLogin user, String clientIp) {
        user.setLastLoginTime(LocalDateTime.now());
        user.setLastLoginIp(clientIp);
        manageUserLoginMapper.updateById(user);
    }
}
